package lar.system.opengl;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.HashMap;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11;

import lar.system.engine.GameLoop;
import lar.system.engine.MainProgram;
import lar.system.engine.Monitor;
import lar.system.engine.UI;

import lar.system.R;


import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.opengl.GLES20;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Toast;

public class GLRenderer implements Renderer
{
	private Context 	context;
	private int[] 		textures;
	//private int 		textureCount;
	//private int[] 		textureResourceIds;
	private final int	maxTextures = 500;
	private GLImage 	keyboard=null;
	private HashMap<Integer, Integer> 	rtt; //resource to tex
	
	public GLRenderer(Context context) 
	{
		this.context = context;
	}

	public int ResourceIdToTexture(int resourceId)
	{
		Integer ans = rtt.get(resourceId);
		if(ans != null) return ans.intValue();
		return -1;
		/*
		for(int i = 0; i < textureCount; i++) 
			if(textureResourceIds[i] == resourceId) return textures[i];
		return -1;*/
	}
	
	public void moveCamera(float dx, float dy, float dz)
	{
		
	}
	public void onDrawFrame(GL10 gl) 
	{
		gl.glClearColor(0f, 0f, 0f, 0f); 
		gl.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();
		if(!UI.loaded) 
		{
			return;
		}
		//Log.d("R", "rendering.. " + Math.random());
		synchronized(GameLoop.stateLock)
		{
			//gl.glClearColor(0.0f, 0.06f, 0.3f, 1.0f);
			UI.needRendering = false;
			
			if(MainProgram.gameLoop != null && MainProgram.gameLoop.getState() != null) 
			{
				try 
				{
					MainProgram.gameLoop.getState().Draw((GL11)gl);
				}
				catch(Exception e)
				{
					UI.loaded = false;
					StringWriter sw = new StringWriter();
					PrintWriter pw = new PrintWriter(sw);
					e.printStackTrace(pw);
					MainProgram.MessageBox(sw.toString()); 
					e.printStackTrace();
					
				}
			}
			if(UI.keyboard)
			{
				float asp = MainProgram.glSurfaceView.getAspectRatio();
				if(keyboard == null) keyboard = new GLImage(R.drawable.kb, new float[]{0,-0.6f}, new float[]{asp, 0.4f});
				keyboard.Draw((GL11)gl);
			}
		}
		
		
	}
	
	

	public void onSurfaceChanged(GL10 gl, int width, int height) 
	{
		
		if(height == 0) { 					
			height = 1; 					
		}
		
		gl.glViewport(0, 0, width, height); 
		gl.glMatrixMode(GL11.GL_PROJECTION); 
		gl.glLoadIdentity(); 				

		GLU.gluPerspective(gl, 90.0f, MainProgram.glSurfaceView.getAspectRatio(), 0.1f, 700.0f);

		gl.glMatrixMode(GL11.GL_MODELVIEW); 
		gl.glLoadIdentity(); 	
		
	}

	public void onSurfaceCreated(GL10 gl10, EGLConfig config) 
	{
		rtt = new HashMap<Integer, Integer>(200);
		textures = new int[maxTextures];
		//textureResourceIds = new int[maxTextures];
		//for(int i = 0; i < maxTextures; i++) textureResourceIds[i] = -1;
		//textureCount = 0;
		
		GL11 gl = (GL11)gl10;
		gl.glEnable(GL11.GL_TEXTURE_2D);			
		gl.glShadeModel(GL11.GL_SMOOTH); 			
		gl.glClearColor(0.0f, 0.0f, 1.0f, 1.0f); 	
		gl.glClearDepthf(1.0f); 					
		gl.glEnable(GL11.GL_DEPTH_TEST); 			
		gl.glDepthFunc(GL11.GL_LEQUAL); 
		gl.glEnable(GL11.GL_BLEND);
	    gl.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); 
		gl.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
		
		loadGLTexture(gl, R.drawable.basefont);
		MainProgram.glSurfaceView.requestRender();
		/*
		 * LOAD GL TEXTURES HERE (MEANWHILE)
		 */
		//loadGLTexture(gl, R.drawable.mainmenu_02);
		//loadGLTexture(gl, R.drawable.alpha);
		//loadGLTexture(gl, R.drawable.exit);
		//loadGLTexture(gl, R.drawable.skirmish);
		//loadGLTexture(gl, R.drawable.back);
		loadGLTexture(gl, R.drawable.icon);
		
		loadGLTexture(gl, R.drawable.desert1);
		loadGLTexture(gl, R.drawable.desert2);
		loadGLTexture(gl, R.drawable.desert3);
		loadGLTexture(gl, R.drawable.desert4);
		loadGLTexture(gl, R.drawable.desert5);
		
		loadGLTexture(gl, R.drawable.lush1);
		loadGLTexture(gl, R.drawable.lush2);
		loadGLTexture(gl, R.drawable.lush3);
		loadGLTexture(gl, R.drawable.lush4);
		loadGLTexture(gl, R.drawable.lush5);
		
		loadGLTexture(gl, R.drawable.snow1);
		loadGLTexture(gl, R.drawable.snow2);
		loadGLTexture(gl, R.drawable.snow3);
		loadGLTexture(gl, R.drawable.snow4);
		loadGLTexture(gl, R.drawable.snow5);
		
		loadGLTexture(gl, R.drawable.mars1); 
		loadGLTexture(gl, R.drawable.mars2);
		loadGLTexture(gl, R.drawable.mars3); 
		loadGLTexture(gl, R.drawable.mars4); 
		loadGLTexture(gl, R.drawable.mars5); 
		
		loadGLTexture(gl, R.drawable.shell); 
		loadGLTexture(gl, R.drawable.exp);
		loadGLTexture(gl, R.drawable.resume);
		loadGLTexture(gl, R.drawable.bar_left);
		loadGLTexture(gl, R.drawable.bar_right);
		loadGLTexture(gl, R.drawable.bar_bottom);
		loadGLTexture(gl, R.drawable.bar_top);
		loadGLTexture(gl, R.drawable.tank);
		loadGLTexture(gl, R.drawable.turretmuzzle);
		loadGLTexture(gl, R.drawable.line);
		loadGLTexture(gl, R.drawable.down);
		loadGLTexture(gl, R.drawable.up);
		loadGLTexture(gl, R.drawable.left);
		loadGLTexture(gl, R.drawable.right);
		loadGLTexture(gl, R.drawable.dialog1);
		loadGLTexture(gl, R.drawable.button); 
		loadGLTexture(gl, R.drawable.fire);
		loadGLTexture(gl, R.drawable.firepressed);
		loadGLTexture(gl, R.drawable.parachute);
		loadGLTexture(gl, R.drawable.horeshwheel);
		loadGLTexture(gl, R.drawable.powerhandle);
		loadGLTexture(gl, R.drawable.powerscale);
		loadGLTexture(gl, R.drawable.gridbg);
		loadGLTexture(gl, R.drawable.mbox);
		loadGLTexture(gl, R.drawable.mainbg);
		loadGLTexture(gl, R.drawable.clear);
		loadGLTexture(gl, R.drawable.detoshield);
		loadGLTexture(gl, R.drawable.magnet);
		loadGLTexture(gl, R.drawable.buttonpressed);
		loadGLTexture(gl, R.drawable.button2);
		loadGLTexture(gl, R.drawable.button2pressed);
		loadGLTexture(gl, R.drawable.sounds);
		loadGLTexture(gl, R.drawable.music);
		loadGLTexture(gl,  R.drawable.tracers);
		loadGLTexture(gl, R.drawable.kb);
		//loadGLTexture(gl, R.drawable.circle);
		loadGLTexture(gl, R.drawable.dust1);
		loadGLTexture(gl, R.drawable.dust2);
		loadGLTexture(gl, R.drawable.dust3);
		loadGLTexture(gl, R.drawable.dome);
		loadGLTexture(gl, R.drawable.shell_t);
		loadGLTexture(gl, R.drawable.chat);
		loadGLTexture(gl, R.drawable.chatbig);
		loadGLTexture(gl, R.drawable.chat2);
		loadGLTexture(gl, R.drawable.missile);
		loadGLTexture(gl, R.drawable.shield);
		loadGLTexture(gl, R.drawable.health);
		loadGLTexture(gl, R.drawable.armor);
		loadGLTexture(gl, R.drawable.mag);
		
		UI.loaded = true;
		MainProgram.gameLoopThread.interrupt();
	}
	
	public static int nextPowerOfTwo(int num)
	{
		for(int i = 0; true; i++)
		{
			double x = Math.pow(2, (double)i);
			if(x >= num) return (int)x;
		}
	}
	
	public void loadGLTexture(GL11 gl, Bitmap bitmap, int resourceId, boolean recycle)
	{
		Bitmap newBitmap = Bitmap.createBitmap(nextPowerOfTwo(bitmap.getWidth()), 
				nextPowerOfTwo(bitmap.getHeight()), bitmap.getConfig());
		
		
		//int x=0,y=0,width=bitmap.getWidth(),height=bitmap.getHeight();
		
		Canvas canvas = new Canvas(newBitmap);

		canvas.drawBitmap(bitmap, 0, 0, null);
		//old memory-expensive code 
		//int [] pixels = new int[width * height];
		//bitmap.getPixels(pixels, 0, width, x, y, width, height);
		//newBitmap.setPixels(pixels, 0, width, x, y, width, height);
		if(recycle) bitmap.recycle();
		gl.glGenTextures(1, textures, rtt.size());
		gl.glBindTexture(GL11.GL_TEXTURE_2D, textures[rtt.size()]);
		rtt.put(resourceId, textures[rtt.size()]);
		//textureResourceIds[textureCount] = resourceId;
		
		//textureCount++;
		
		gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
		gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
		gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
	    gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
	    //GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, newBitmap, 0);
	    
	    try
	    {
	    	myTexImage2D(gl, newBitmap);
	    	//Log.d("R", "TexImage2D success");
	    }
	    catch(OutOfMemoryError e)
	    {
	    	//Log.d("R", "No memory for texImage2D");
	    	GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, newBitmap, 0);
	    }
	    
		
		newBitmap.recycle();
	}
	
	public void loadGLTexture(GL11 gl, int resourceId) 
	{
		Bitmap bitmap = BitmapFactory.decodeResource(this.context.getResources(), resourceId);
		loadGLTexture(gl, bitmap, resourceId, true);
	}

	
	/*
	 * EXPERIMENTAL
	 */
	private void myTexImage2D(GL10 gl, Bitmap bitmap) 
	{
		// Don't loading using GLUtils, load using gl-method directly
		// GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
		int[] pixels = extractPixels(bitmap);
		byte[] pixelComponents = new byte[pixels.length*4];
		int byteIndex = 0;
		for (int i = 0; i < pixels.length; i++) {
			int p = pixels[i];
			// Convert to byte representation RGBA required by gl.glTexImage2D.
			// We don't use intbuffer, because then we
			// would be relying on the intbuffer wrapping to write the ints in
			// big-endian format, which means it would work for the wrong
			// reasons, and it might brake on some hardware.
			pixelComponents[byteIndex++] = (byte) ((p >> 16) & 0xFF); // red
			pixelComponents[byteIndex++] = (byte) ((p >> 8) & 0xFF); //green
			pixelComponents[byteIndex++] = (byte) ((p) & 0xFF); // blue
			pixelComponents[byteIndex++] = (byte) (p >> 24);  // alpha
		}
		ByteBuffer pixelBuffer = ByteBuffer.wrap(pixelComponents);

		gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(), 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuffer);
	}
	private static int[] extractPixels(Bitmap src) 
	{
        int x = 0;
        int y = 0;
        int w = src.getWidth();
        int h = src.getHeight();
        int[] colors = new int[w * h];
        src.getPixels(colors, 0, w, x, y, w, h);
        return colors;
	} 
	
	public static int[] glCoordsToWindowCoords(float[] glCoords)
	{
		int w = MainProgram.glSurfaceView.getWidth();
		int h = MainProgram.glSurfaceView.getHeight();
		float asp = MainProgram.glSurfaceView.getAspectRatio();
		return new int[]
				{
					(int)((1+(glCoords[0]/asp))/2)*w,
					(int)((1+glCoords[1])/2)*h
				};
	}
	public static int[] glCoordsToWindowCoords(float x, float y)
	{
		int w = MainProgram.glSurfaceView.getWidth();
		int h = MainProgram.glSurfaceView.getHeight();
		float asp = MainProgram.glSurfaceView.getAspectRatio();
		return new int[]
				{
					(int)(((1+(x/asp))/2)*w),
					(int)(((1+y)/2)*h)
				};
	}
	
	public static int[] getScissorCoords(float[] glPos, float[] glDim)
	{
		int[] sc = GLRenderer.glCoordsToWindowCoords(glPos[0]-glDim[0], glPos[1]-glDim[1]);
		int[] sc2 = GLRenderer.glCoordsToWindowCoords(glPos[0]+glDim[0], glPos[1]+glDim[1]);
		return new int[]{sc[0],sc[1],sc2[0]-sc[0],sc2[1]-sc[1]};
	}
	
}
